﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;

namespace IQIGame.Onigao.Framework
{
    /// <summary>
    /// POD对象池, 可以多线程并发调用, 使用之前需要手动Init
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class PODPool<T> : PODPool where T : class, IPOD, new()
    {
        /// <summary>
        /// 因为pod是跨线程Get Put, 所以需要考虑并发问题
        /// </summary>
        private readonly ConcurrentQueue<T> _pool = new ();
        /// <summary>
        /// 最大数量, 手动初始化
        /// </summary>
        private int _maxCount;
        /// <summary>
        /// 当前数量, 最终会和pools.Count保持一致, 会偶尔少量(取决于操作的线程数量)超过maxCount
        /// </summary>
        private volatile int _nowCount;

        private static readonly PODPool<T> Instance = new ();
        private PODPool() { }

        public static Func<T> Init(int maxCount)
        {
            Instance._maxCount = (int)(maxCount * Factor);
            return Instance._Get;
        }

        public static T Get()
        {
            return Instance._Get();
        }

        public static void Put(T pod)
        {
            Instance._Put(pod);
        }

        public static void Clear()
        {
            Instance.ClearPool();
        }

        public static int PoolCount => Instance._nowCount;

        private T _Get()
        {
            _pool.TryDequeue(out T result);
            if (result == null)
            {
                return new T();
            }
            Interlocked.Decrement(ref _nowCount);
            //UnityEngine.Debug.LogWarning($"{typeof(PODPool<T>)} is Get Success");
            return result;
        }

        private void _Put(T data)
        {
            if (_maxCount == 0)
            {
                UnityEngine.Debug.LogError($"{typeof(PODPool<T>)} is not Init");
                return;
            }
            if (data == null)
            {
                LogGame.LogError($"{typeof(PODPool<T>)} Put data is null");
                return;
            }
#if UNITY_EDITOR
            if (data.GetType() != typeof(T))
            {
                UnityEngine.Debug.LogError($"{typeof(PODPool<T>)} Put Type Error, Current Type:" + typeof(T).Name + " Real Type:" + data.GetType().Name);
                return;
            }
#endif
            bool canPutIntoPool = _nowCount < _maxCount;
#if UNITY_EDITOR
            //if (!canPutIntoPool)
            //{
            //    UnityEngine.Debug.LogWarning($"{typeof(PODPool<T>)}, MaxStore：{_maxCount}");
            //}
#endif
            data.Clear();
#if UNITY_EDITOR
            lock (_pool)
#endif
            {
#if UNITY_EDITOR
                foreach (var item in _pool)
                {
                    if (ReferenceEquals(item, data))//需要用ReferenceEquals,防止重写==或equals
                    {
                        UnityEngine.Debug.LogError($"{typeof(PODPool<T>)} Put Error");
                        return;
                    }
                }
#endif
                if (canPutIntoPool)
                {
                    Interlocked.Increment(ref _nowCount);
                    _pool.Enqueue(data);
                }
            }
        }

        protected override void ClearPool()
        {
            _pool.Clear();
            _nowCount = 0;
        }

        protected override void PutData(IPOD pod)
        {
            _Put(pod as T);
        }

        protected override Type PoolType => typeof(T);
    }

    public abstract class PODPool
    {
        /// <summary>
        /// 容量系数, 放到不同的环境下系数可能会有变化
        /// </summary>
        public static double Factor = 1;
        /// <summary>
        /// 所有的PODPool
        /// </summary>
        private readonly static Dictionary<Type, PODPool> _allPools = new();

        protected PODPool()
        {
            _allPools.Add(PoolType, this);
        }

        public static void ClearAll()
        {
            foreach (var pool in _allPools.Values)
            {
                pool.ClearPool();
            }
        }

        protected abstract void ClearPool();
        protected abstract void PutData(IPOD pod);
        protected abstract Type PoolType { get; }
        
    }
}
